#pragma once // 确保头文件只被包含一次，避免重复定义

#include <iostream>        // 包含标准输入输出流库
#include <string>          // 包含字符串操作库
#include <curl/curl.h>     // 包含 libcurl 库，用于执行 HTTP 请求
#include <fstream>         // 包含文件操作库
#include <filesystem>      // 包含文件系统操作库
#include "ProgressBar.hpp" // 包含 ProgressBar 类的定义，用于显示下载进度

// 定义一个名为 CurlHandler 的类，用于处理基于 libcurl 的网络请求
class CurlHandler
{
private:
    CURL *_curl;               // libcurl 的会话句柄，用于执行网络请求
    std::string _url;          // 存储目标 URL
    ProgressBar *_progressBar; // 指向 ProgressBar 对象的指针，用于显示下载进度

public:
    // 构造函数，初始化成员变量并创建 libcurl 会话
    CurlHandler(const std::string &url, ProgressBar *progressBar) : _url(url), _progressBar(progressBar)
    {
        _curl = curl_easy_init(); // 初始化 libcurl 会话
    }

    // 析构函数，清理 libcurl 会话
    ~CurlHandler()
    {
        if (_curl) // 如果 curl 会话句柄有效
        {
            curl_easy_cleanup(_curl); // 清理 libcurl 会话
        }
    }

    // 执行网络请求并处理响应
    void perform(const std::string &outputDir)
    {
        if (!_curl) // 如果 curl 会话句柄无效，直接返回
            return;

        // 从 URL 中提取文件名
        std::string fileName = _url.substr(_url.find_last_of('/') + 1);
        std::string outputFilePath = outputDir + "/" + fileName;

        // 确保目标目录存在
        if (!std::filesystem::exists(outputDir))
        {
            std::filesystem::create_directories(outputDir);
        }

        std::ofstream outputFile(outputFilePath, std::ios::binary); // 打开文件以二进制模式写入
        if (!outputFile.is_open())
        {
            std::cerr << "无法打开文件 " << outputFilePath << std::endl;
            return;
        }

        // 设置 libcurl 的选项
        curl_easy_setopt(_curl, CURLOPT_URL, _url.c_str());                  // 设置目标 URL
        curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, WriteCallback);       // 设置写回调函数
        curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &outputFile);             // 设置写回调函数的用户数据
        curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, 0L);                     // 启用进度回调
        curl_easy_setopt(_curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback); // 设置进度回调函数
        curl_easy_setopt(_curl, CURLOPT_XFERINFODATA, _progressBar);         // 设置进度回调函数的用户数据

        // 执行网络请求
        CURLcode res = curl_easy_perform(_curl); // 调用 libcurl 执行请求
        if (res != CURLE_OK)                     // 如果请求失败
        {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; // 输出错误信息
        }
        else
        {
            _progressBar->Done(); // 请求成功，调用 ProgressBar 的 Done 方法
        }

        outputFile.close(); // 关闭文件
    }

    // 写回调函数，用于处理 libcurl 接收到的数据
    static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
    {
        std::ofstream *outputFile = static_cast<std::ofstream *>(userp); // 将 userp 转换为 std::ofstream 类型
        outputFile->write(static_cast<char *>(contents), size * nmemb);  // 将数据写入文件
        return size * nmemb;                                             // 返回处理的数据大小
    }

    // 进度回调函数，用于更新下载进度
    static int ProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
    {
        ProgressBar *progressBar = static_cast<ProgressBar *>(clientp); // 将 clientp 转换为 ProgressBar 类型
        if (dltotal > 0)                                                // 如果总下载量已知
        {
            progressBar->update(static_cast<int>(dlnow)); // 更新进度条
        }
        else
        {
            progressBar->update(static_cast<int>(dlnow)); // 如果总下载量未知，只显示已下载的字节数
        }
        return 0; // 返回 0 表示回调成功
    }
};